const Signal = require("./signal")

const NodeHandler = {
    Program (nodeIterator) {
        for (const node of nodeIterator.node.body) {
            nodeIterator.traverse(node)
        }
    },
    VariableDeclaration (nodeIterator) {
        const kind = nodeIterator.node.kind
        for (const declaration of nodeIterator.node.declarations) {
            const { name } = declaration.id
            const value = declaration.init ? nodeIterator.traverse(declaration.init) : undefined
            // 在作用域当中定义变量
            if (nodeIterator.scope.type === 'block' && kind === 'var') {
                nodeIterator.scope.parentScope.declare(name, value, kind)
            } else {
                nodeIterator.scope.declare(name, value, kind)
            }
        }
    },
    ExpressionStatement (nodeIterator) {
        return nodeIterator.traverse(nodeIterator.node.expression)
    },
    MemberExpression (nodeIterator) {
        const obj = nodeIterator.traverse(nodeIterator.node.object)
        const name = nodeIterator.node.property.name
        return obj[name]
    },
    CallExpression (nodeIterator) {
        const func = nodeIterator.traverse(nodeIterator.node.callee)
        const args = nodeIterator.node.arguments.map(arg => nodeIterator.traverse(arg))
        let value
        if (nodeIterator.node.callee.type === 'MemberExpression') {
            value = nodeIterator.traverse(nodeIterator.node.callee.object)
        }
        return func.apply(value, args)
    },
    Identifier (nodeIterator) {
        if (nodeIterator.node.name === 'undefined') {
            return undefined
        }
        return nodeIterator.scope.get(nodeIterator.node.name).value
    },
    Literal (nodeIterator) {
        return nodeIterator.node.value
    },
    BinaryExpressionOperatortraverseMap: {
        '==': (a, b) => a == b,
        '!=': (a, b) => a != b,
        '===': (a, b) => a === b,
        '!==': (a, b) => a !== b,
        '<': (a, b) => a < b,
        '<=': (a, b) => a <= b,
        '>': (a, b) => a > b,
        '>=': (a, b) => a >= b,
        '<<': (a, b) => a << b,
        '>>': (a, b) => a >> b,
        '>>>': (a, b) => a >>> b,
        '+': (a, b) => a + b,
        '-': (a, b) => a - b,
        '*': (a, b) => a * b,
        '/': (a, b) => a / b,
        '%': (a, b) => a % b,
        '**': (a, b) => { throw new Error('canjs: es5 doesn\'t supports operator "**"') },
        '|': (a, b) => a | b,
        '^': (a, b) => a ^ b,
        '&': (a, b) => a & b,
        'in': (a, b) => a in b,
        'instanceof': (a, b) => a instanceof b
    },
    BinaryExpression (nodeIterator) {
        const a = nodeIterator.traverse(nodeIterator.node.left)
        const b = nodeIterator.traverse(nodeIterator.node.right)
        return NodeHandler.BinaryExpressionOperatortraverseMap[nodeIterator.node.operator](a, b)
    },
    FunctionExpression (nodeIterator) {
        const node = nodeIterator.node
        /**
         * 1、定义函数需要先为其定义一个函数作用域，且允许继承父级作用域
         * 2、注册`this`, `arguments`和形参到作用域的变量空间
         * 3、检查return关键字
         * 4、定义函数名和长度
         */
        const fn = function () {
            const scope = nodeIterator.createScope('function')
            scope.constDeclare('this', this)
            scope.constDeclare('arguments', arguments)

            node.params.forEach((param, index) => {
                const name = param.name
                scope.varDeclare(name, arguments[index])
            })

            const signal = nodeIterator.traverse(node.body, { scope })
            if (Signal.isReturn(signal)) {
                return signal.value
            }
        }

        Object.defineProperties(fn, {
            name: { value: node.id ? node.id.name : '' },
            length: { value: node.params.length }
        })

        return fn
    },
}

module.exports = NodeHandler
